﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace JY.Label.PrintHelper
{

    public class KeyValue
    {
        public char Key { get; set; }
        public int Value { get; set; }
    }
    public static class LabelConvertCmdToImg
    {
        /// <summary>
        /// ZPL压缩字典
        /// </summary>
        private static List<KeyValue> compressDictionary = new List<KeyValue>();
       

        private static int GetRowSize(int graphWidth)
        {
            return (((graphWidth) + 31) >> 5) << 2;
        }
        private static int GetRowRealBytesCount(int graphWidth)
        {
            if ((graphWidth % 8) > 0)
            {
                return graphWidth / 8 + 1;
            }
            else
            {
                return graphWidth / 8;
            }
        }

        #region 静态构造方法
        static LabelConvertCmdToImg()
        {
            initCompressCode();
        }

        #endregion

        private static void initCompressCode()
        {
            //G H I J K L M N O P Q R S T U V W X Y        对应1,2,3,4……18,19。
            //g h i j k l m n o p q r s t u v w x y z      对应20,40,60,80……340,360,380,400。            
            for (int i = 0; i < 19; i++)
            {
                compressDictionary.Add(new KeyValue() { Key = Convert.ToChar(71 + i), Value = i + 1 });
            }
            for (int i = 0; i < 20; i++)
            {
                compressDictionary.Add(new KeyValue() { Key = Convert.ToChar(103 + i), Value = (i + 1) * 20 });
            }
        }
        #region 生成ZPL图像打印指令
        public static byte[] getZPLBytes(byte[] graphBuffer)
        {
            int graghWidth = 0;
            int graghHeight = 0;
            byte[] bmpData = getBitmapData(graphBuffer, out graghWidth, out graghHeight);
            int rowRealBytesCount = GetRowRealBytesCount(graghWidth);
            int bmpDataLength = bmpData.Length;
            for (int i = 0; i < bmpDataLength; i++)
            {
                bmpData[i] ^= 0xFF;
            }

            string copiesString = string.Empty;
            string textHex = BitConverter.ToString(bmpData).Replace("-", string.Empty);
            string textBitmap =  CompressLZ77(textHex);

            //TODO 此处可能有问题
            //for (int i = 0; i < Copies; i++)
            //{
            //    copiesString += "^XA^FO0,0^XGR:IMAGE.GRF,1,1^FS^XZ";
            //}
            string text = string.Format("~DGR:IMAGE.GRF,{0},{1},{2}{3}^IDR:IMAGE.GRF",
                graghHeight * rowRealBytesCount,
                rowRealBytesCount,
                textBitmap,
                copiesString);
            return Encoding.UTF8.GetBytes(text);
        }
        #endregion

        #region 生成EPL图像打印指令
        public static byte[] getEPLBytes(byte[] graphBuffer)
        {
            int graghWidth = 0;
            int graghHeight = 0;
            byte[] buffer = getBitmapData(graphBuffer, out graghWidth, out graghHeight);

            int rowRealBytesCount = GetRowRealBytesCount(graghWidth);
            string text = string.Format("N\r\nGW{0},{1},{2},{3},{4}\r\nP{5},1\r\n",
                0,
                0,
                rowRealBytesCount,
                graghHeight,
                Encoding.GetEncoding("iso-8859-1").GetString(buffer),
                0);
            return Encoding.GetEncoding("iso-8859-1").GetBytes(text);
        }
        #endregion

        #region 获取单色位图数据
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pimage"></param>
        /// <returns></returns>
        public static Bitmap ConvertToGrayscale(Bitmap pimage)
        {
            Bitmap source = null;

            // If original bitmap is not already in 32 BPP, ARGB format, then convert
            if (pimage.PixelFormat != PixelFormat.Format32bppArgb)
            {
                source = new Bitmap(pimage.Width, pimage.Height, PixelFormat.Format32bppArgb);
                source.SetResolution(pimage.HorizontalResolution, pimage.VerticalResolution);
                using (Graphics g = Graphics.FromImage(source))
                {
                    g.DrawImageUnscaled(pimage, 0, 0);
                }
            }
            else
            {
                source = pimage;
            }

            // Lock source bitmap in memory
            BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

            // Copy image data to binary array
            int imageSize = sourceData.Stride * sourceData.Height;
            byte[] sourceBuffer = new byte[imageSize];
            Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);

            // Unlock source bitmap
            source.UnlockBits(sourceData);

            // Create destination bitmap
            Bitmap destination = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);

            // Lock destination bitmap in memory
            BitmapData destinationData = destination.LockBits(new Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);

            // Create destination buffer
            imageSize = destinationData.Stride * destinationData.Height;
            byte[] destinationBuffer = new byte[imageSize];

            int sourceIndex = 0;
            int destinationIndex = 0;
            int pixelTotal = 0;
            byte destinationValue = 0;
            int pixelValue = 128;
            int height = source.Height;
            int width = source.Width;
            int threshold = 500;

            // Iterate lines
            for (int y = 0; y < height; y++)
            {
                sourceIndex = y * sourceData.Stride;
                destinationIndex = y * destinationData.Stride;
                destinationValue = 0;
                pixelValue = 128;

                // Iterate pixels
                for (int x = 0; x < width; x++)
                {
                    // Compute pixel brightness (i.e. total of Red, Green, and Blue values)
                    pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] + sourceBuffer[sourceIndex + 3];
                    if (pixelTotal > threshold)
                    {
                        destinationValue += (byte)pixelValue;
                    }
                    if (pixelValue == 1)
                    {
                        destinationBuffer[destinationIndex] = destinationValue;
                        destinationIndex++;
                        destinationValue = 0;
                        pixelValue = 128;
                    }
                    else
                    {
                        pixelValue >>= 1;
                    }
                    sourceIndex += 4;
                }
                if (pixelValue != 128)
                {
                    destinationBuffer[destinationIndex] = destinationValue;
                }
            }

            // Copy binary image data to destination bitmap
            Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);

            // Unlock destination bitmap
            destination.UnlockBits(destinationData);

            // Dispose of source if not originally supplied bitmap
            if (source != pimage)
            {
                source.Dispose();
            }

            // Return
            return destination;
        }
        /// <summary>
        /// 获取单色位图数据(1bpp)，不含文件头、信息头、调色板三类数据。
        /// </summary>
        /// <returns></returns>
        private static byte[] getBitmapData(byte[] graphBuffer, out int graghWidth, out int graghHeight)
        {
            MemoryStream srcStream = new MemoryStream();
            MemoryStream dstStream = new MemoryStream();
            Bitmap srcBmp = null;
            Bitmap dstBmp = null;
            byte[] srcBuffer = null;
            byte[] dstBuffer = null;
            byte[] result = null;
            try
            {
                srcStream = new MemoryStream(graphBuffer);
                srcBmp = Bitmap.FromStream(srcStream) as Bitmap;
                srcBuffer = srcStream.ToArray();
                graghWidth = srcBmp.Width;
                graghHeight = srcBmp.Height;

                var rowRealBytesCount = GetRowRealBytesCount(graghWidth);
                var rowSize = GetRowSize(graghWidth);
                //dstBmp = srcBmp.Clone(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), PixelFormat.Format1bppIndexed);
                dstBmp = ConvertToGrayscale(srcBmp);
                dstBmp.Save(dstStream, ImageFormat.Bmp);
                dstBuffer = dstStream.ToArray();

                int bfOffBits = BitConverter.ToInt32(dstBuffer, 10);
                result = new byte[graghHeight * rowRealBytesCount];

                //读取时需要反向读取每行字节实现上下翻转的效果，打印机打印顺序需要这样读取。
                for (int i = 0; i < graghHeight; i++)
                {
                    Array.Copy(dstBuffer, bfOffBits + (graghHeight - 1 - i) * rowSize, result, i * rowRealBytesCount, rowRealBytesCount);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
            finally
            {
                if (srcStream != null)
                {
                    srcStream.Dispose();
                    srcStream = null;
                }
                if (dstStream != null)
                {
                    dstStream.Dispose();
                    dstStream = null;
                }
                if (srcBmp != null)
                {
                    srcBmp.Dispose();
                    srcBmp = null;
                }
                if (dstBmp != null)
                {
                    dstBmp.Dispose();
                    dstBmp = null;
                }
            }
            return result;
        }
        #endregion

        #region LZ77图像字节流压缩方法
        public static string CompressLZ77(string text)
        {
            //将转成16进制的文本进行压缩
            string result = string.Empty;
            char[] arrChar = text.ToCharArray();
            int count = 1;
            for (int i = 1; i < text.Length; i++)
            {
                if (arrChar[i - 1] == arrChar[i])
                {
                    count++;
                }
                else
                {
                    result += convertNumber(count) + arrChar[i - 1];
                    count = 1;
                }
                if (i == text.Length - 1)
                {
                    result += convertNumber(count) + arrChar[i];
                }
            }
            return result;
        }

        public static string DecompressLZ77(string text)
        {
            string result = string.Empty;
            char[] arrChar = text.ToCharArray();
            int count = 0;
            for (int i = 0; i < arrChar.Length; i++)
            {
                if (isHexChar(arrChar[i]))
                {
                    //十六进制值
                    result += new string(arrChar[i], count == 0 ? 1 : count);
                    count = 0;
                }
                else
                {
                    //压缩码
                    int value = GetCompressValue(arrChar[i]);
                    count += value;
                }
            }
            return result;
        }

        private static int GetCompressValue(char c)
        {
            int result = 0;
            for (int i = 0; i < compressDictionary.Count; i++)
            {
                if (c == compressDictionary[i].Key)
                {
                    result = compressDictionary[i].Value;
                }
            }
            return result;
        }

        private static bool isHexChar(char c)
        {
            return c > 47 && c < 58 || c > 64 && c < 71 || c > 96 && c < 103;
        }

        private static string convertNumber(int count)
        {
            //将连续的数字转换成LZ77压缩代码，如000可用I0表示。
            string result = string.Empty;
            if (count > 1)
            {
                while (count > 0)
                {
                    for (int i = compressDictionary.Count - 1; i >= 0; i--)
                    {
                        if (count >= compressDictionary[i].Value)
                        {
                            result += compressDictionary[i].Key;
                            count -= compressDictionary[i].Value;
                            break;
                        }
                    }
                }
            }
            return result;
        }
        #endregion
    }
}
